#ifndef _H_REGISTERS
#define _H_REGISTERS


/*
 * ================== Class Definitions
 */


/*
 * ================== Register Base Class
 */

class Register
{
  uint8_t address;
  uint8_t numDevices;
  uint16_t data[MAXDEVICES];
  uint8_t* dP;
 private:
  // create a device address from a 1-based device number.
  uint8_t deviceNum2Address(uint8_t num);
  void printError(uint8_t deviceNum,char*);

public:
  Register(uint8_t nd);
  void init(uint8_t addr);
  uint8_t getAddress() {return address;}
  void setNumDevices(uint8_t nd);
  uint8_t getNumDevices() {return numDevices;}
  uint16_t getValue(uint8_t deviceNumber);
  void addData(uint8_t d);
  static boolean isAlarm(uint8_t dataCheck);
  static boolean isPecError(uint8_t dataCheck);

  // read from all devices
  boolean readFromDevice();

  // Write the value argument to the device number (1-based).
  // deviceNum == 0 => broadcast. Only MAXDEVICES allowed
  boolean writeDevice(uint16_t value, uint8_t deviceNum);

  // write value to all devices.
  boolean writeAllDevices(uint16_t value);

  virtual void print();
};

/*
 * ================== Register STATUS
 */
// STATUS register class (0x02)
class RegStatus : public Register
{
public:
  RegStatus(uint8_t numDevices);

  // read from all devices
  boolean read();

  // clear all status flags.
  boolean clearStatus();

  boolean clearStatus(uint8_t deviceNum);

  // Is reset flag on?
  boolean isReset(uint8_t deviceNum);
  
  // Is there an Alert-Ack
  boolean isAlrtAck(uint8_t deviceNum);

  // return deviceNumber if any have alrt-ack or 0.
  uint8_t isAlrtAck();

  // Is there an undervoltage condition.
  boolean isUnderVoltage(uint8_t deviceNum);

  // Is there an undervoltage condition. Check all devices.
  boolean isUnderVoltage();

};

/*
 * ================== Register CELLEN
 */
// CELLEN register class (0x09)
// Set to 1, D0 for Cell #1 till D11 for Cell #12 
class RegCellen : public Register
{
public:
  static const uint16_t AllCellsEnabled = 0x0FFF;
public:
  RegCellen(uint8_t numDevices);
  boolean enableAllCells();

  // enable only those cells passed in mask, for all devices
  boolean enableCells(uint16_t cellNumberMask);

  // enable only those cells passed in mask for the device.
  boolean enableCells(uint16_t cellNumberMask, uint8_t deviceNum);

  static void setEnableCellMask(uint16_t& cellMask, uint8_t cellNumber);
  static void setDisableCellMask(uint16_t& cellMask, uint8_t cellNumber);
  static boolean isCellDisabled(uint16_t cellMask, uint8_t cellNumber) {
    return ((cellMask & ((uint16_t)1 << (cellNumber-1))) == 0); }
  static void setEnableAllCellMask(uint16_t& cellMask);
};

/*
 * ================== Register SCANCTRL
 */
// SCANCTRL register (0x0D), only low bit
// Set D0 to 1 to enable, 0 to disable.
class RegScanctrl : public Register
{
public:
  RegScanctrl(uint8_t numDevices);
  boolean enable();
  boolean disable();
};

/*
 * ================== RegCellData
 */
/*
 Reading values is done by broadcasting a read for all CELL#1, which
 returns Cell#1 info for all devices. We'd continue the process for all
 Cells after which we would have an array of 12 such registers.
*/

// RegCellData includes registers 0x20 - 0x2B (for cells 1- 12).
// Has to be initialized for cell number (1-12). 
// Assume this is only going to be read in broadcast mode, that is
// for all devices. Class allocates storage for those many devices.
// Use 2 bytes for every device 

class RegCellData : public Register
{
  uint8_t cellNumber;
public:
  RegCellData(uint8_t nd);

  // initialize with Cell Number
   void initCellNumber(uint8_t cn);

  virtual void print();

  uint8_t getCellNumber() {return cellNumber;}
  uint16_t getVoltageValue(uint8_t deviceNumber);
  float getVoltage(uint8_t deviceNumber);
};

/*
 * ================== RegDeviceAddress
 */

// Register to set Device Address (0x01)
class RegDeviceAddress : public Register
{
 public:
  RegDeviceAddress(uint8_t nd);
  boolean setLastAddress();
};


/*
 * ================== RegUVthrSet
 */
class RegUVthrSet : public Register
{
  
 public:
  static const uint16_t DefaultThrValue = 
    (UNDERVOLTAGE_SET_THRESHOLD / ADC_VOLTAGE_RESOLUTION);
  static uint16_t ThrValue;

 public:
  RegUVthrSet(uint8_t nd);

  // BUGBUG: No checking is done on these values.
  static void setThreshold(uint16_t value);
  static void setThreshold(float value);
  static void setDefaultThreshold() {
    ThrValue = DefaultThrValue;
  }

  // Write threshold value
  boolean writeAllDevices();
};

/*
 * ================== RegUVthrClear
 */
class RegUVthrClear : public Register
{
 public:
  static uint16_t ThrValue;
  static const uint16_t DefaultThrValue = 
    (UNDERVOLTAGE_CLEAR_THRESHOLD / ADC_VOLTAGE_RESOLUTION);
 public:
  RegUVthrClear(uint8_t nd);

  // BUGBUG: No checking is done on these values.
  void setThreshold(uint16_t value);
  void setThreshold(float value);
  static void setDefaultThreshold() {
    ThrValue = DefaultThrValue;
  }

  // Write threshold value to all devices
  boolean writeAllDevices();
};

/*
 * ================== RegOVthrSet
 */
class RegOVthrSet : public Register
{
  
 public:
  static const uint16_t DefaultThrValue = 
    (OVERVOLTAGE_SET_THRESHOLD / ADC_VOLTAGE_RESOLUTION);
  static uint16_t ThrValue;

 public:
  RegOVthrSet(uint8_t nd);

  // BUGBUG: No checking is done on these values.
  static void setThreshold(uint16_t value);
  static void setThreshold(float value);
  static void setDefaultThreshold() {
    ThrValue = DefaultThrValue;
  }

  // Write threshold value
  boolean writeAllDevices();
};

/*
 * ================== RegUVthrClear
 */
class RegOVthrClear : public Register
{
 public:
  static uint16_t ThrValue;
  static const uint16_t DefaultThrValue = 
    (OVERVOLTAGE_CLEAR_THRESHOLD / ADC_VOLTAGE_RESOLUTION);
 public:
  RegOVthrClear(uint8_t nd);

  // BUGBUG: No checking is done on these values.
  void setThreshold(uint16_t value);
  void setThreshold(float value);
  static void setDefaultThreshold() {
    ThrValue = DefaultThrValue;
  }

  // Write threshold value to all devices
  boolean writeAllDevices();
};


/*
 * ================== Register ALRTUVEN
 */
// Alert UnderVoltage Enable register class (0x07)
// Set to 1, D0 for Cell #1 till D11 for Cell #12
// Same as CELLEN
class RegAlrtUVen : public Register
{
public:
  RegAlrtUVen(uint8_t numDevices);
  boolean enableAllCells();

  // enable only those cells passed in mask, for all devices
  boolean enableCells(uint16_t cellNumberMask);

  // enable only those cells passed in mask for the device.
  // uses same mask as CELLEN
  boolean enableCells(uint16_t cellNumberMask, uint8_t deviceNum);

};

#endif
